package com.javacoo.xkernel.spi;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

/**
 * 扩展点实现加载器
 * <p>
 * 说明:
 * </p>
 * <li></li>
 * 
 * @author DuanYong
 * @param <T>
 * @since 2019年1月6日上午11:49:28
 */
public class ExtensionLoader<T> {
	private static final String SERVICES_DIRECTORY = "META-INF/services/";
	private static final String EXT_DIRECTORY = "META-INF/ext/";
	private static final String EXT_INTERNAL_DIRECTORY = EXT_DIRECTORY + "internal/";
	private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");

    /** 保存了内核开放的扩展点对应的 ExtensionLoader 实例对象 */
	private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
	/** 保存了扩展类型 （Class） 和扩展类型的实例对象 */
	private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
    /** 保存扩展的名称和实例对象 ， 扩展名称为 key  ， 扩展实例为 value*/
	private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
	/** 被 @SPI 注解的 Interface ， 也就是扩展点 */
	private final Class<?> type;
    /** 满足装饰模式的扩展的 Class 实例*/
	private Set<Class<?>> cachedWrapperClasses;
	/** 保存不满足装饰模式（不存在只有一个参数，并且参数是扩展点类型实例对象的构造函数）的扩展的名称*/
	private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
    /** 保存不满足装饰模式的扩展的 Class 实例 ， 扩展的名称作为 key , Class 实例作为 value*/
	private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
    /**  扩展点上 @SPI 注解指定的缺省适配扩展*/
	private String cachedDefaultName;
    /** 保存在加载扩展点配置文件时，加载扩展点过程中抛出的异常 ， key 是当前读取的扩展点配置文件的一行 ， value 是抛出的异常 */
	private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

	private ExtensionLoader(Class<?> type) {
		this.type = type;
	}

	/**
	 * 根据类型获取扩展点加载器
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param type
	 * @return
	 * @since 2019年1月6日下午9:32:00
	 */
	public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
		if (type == null){
            throw new IllegalArgumentException("Extension type == null");
        }
		if (!type.isInterface()) {
			throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
		}
		if (!withExtensionAnnotation(type)) {
			throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @"
					+ Spi.class.getSimpleName() + " Annotation!");
		}
		// 根据接口对象取ExtensionLoader类
		ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
		if (loader == null) {
			// 如果为空保存接口类对应的 新建的ExtensionLoader对象
			EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
			loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
		}
		return loader;
	}

	private static <T> boolean withExtensionAnnotation(Class<T> type) {
		return type.isAnnotationPresent(Spi.class);
	}

	/**
	 * 通过SPI加载接口对应的所有实现类
	 * <p>
	 * 说明:
	 * </p>
	 * <li>此方法已经getExtensionClasses方法同步过。</li>
	 * 
	 * @author DuanYong
	 * @return
	 * @since 2019年1月6日下午4:27:53
	 */
	private Map<String, Class<?>> loadExtensionClasses() {
		// 所有的扩展点接口都必须被SPI注释标注
		final Spi defaultAnnotation = type.getAnnotation(Spi.class);
		if (defaultAnnotation != null) {
			String value = defaultAnnotation.value();
			if ((value = value.trim()).length() > 0) {
				// 一个@SPI注解的值只能有一个
				String[] names = NAME_SEPARATOR.split(value);
				if (names.length > 1) {
					throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
							+ ": " + Arrays.toString(names));
				}
				// cachedDefaultName表示该扩展点对应的默认适配类的key
				// 逻辑运行到这里就意味着该扩展点有定义的适配类，不需要框架自己生成适配类
				if (names.length == 1){
					cachedDefaultName = names[0];
				}
			}
		}
		// 加载对应目录下的配置文件，三个目录分别为：META-INF/services/，META-INF/ext，META-INF/ext/internal
		Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
		loadDirectory(extensionClasses, EXT_INTERNAL_DIRECTORY);
		loadDirectory(extensionClasses, EXT_DIRECTORY);
		loadDirectory(extensionClasses, SERVICES_DIRECTORY);
		return extensionClasses;
	}

	/**
	 * 加载相关路径下的类文件
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @param extensionClasses
	 * @param dir
	 * @since 2019年1月6日下午4:33:42
	 */
	private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
		String fileName = dir + type.getName();
		try {
			Enumeration<java.net.URL> urls;
			// 加载这些类的classloader要和加载当前类的classloader一致,这个类似与Java默认的类加载器和类的加载关系
			ClassLoader classLoader = findClassLoader();
			if (classLoader != null) {
				// 该步骤就加载所有的classpath下面的同名文件（包含你的项目本地classpath和依赖jar包）
				urls = classLoader.getResources(fileName);
			} else {
				urls = ClassLoader.getSystemResources(fileName);
			}
			if (urls != null) {
				// 一般情况下每个包内只会对与每个扩展点放置一个类信息描述文件
				while (urls.hasMoreElements()) {
					java.net.URL resourceURL = urls.nextElement();
					loadResource(extensionClasses, classLoader, resourceURL);
				}
			}
		} catch (Throwable t) {
			//log.error("加载扩展接口:{}失败, 文件名: {}}).",t,type,fileName,t);
		}
	}
    /**
     * 加载资源
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param extensionClasses 扩展类Map
     * @param classLoader 类加载器
     * @param resourceURL 类资源路径
     * @since 2019年1月6日下午4:36:34
     */
	private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
			java.net.URL resourceURL) {
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
			try {
				String line;
				//每次处理一行
				while ((line = reader.readLine()) != null) {
					//#号以后的为注释
					final int ci = line.indexOf('#');
					//注释去掉
					if (ci >= 0) line = line.substring(0, ci);
					line = line.trim();
					if (line.length() > 0) {
						try {
							String name = null;
							//=号之前的为扩展名字，后面的为扩展类实现的全限定名
							int i = line.indexOf('=');
							if (i > 0) {
								name = line.substring(0, i).trim();
								line = line.substring(i + 1).trim();
							}
							if (line.length() > 0) {
								//加载扩展类的实现
								loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
							}
						} catch (Throwable t) {
							IllegalStateException e = new IllegalStateException(
									"Failed to load extension class(interface: " + type + ", class line: " + line
											+ ") in " + resourceURL + ", cause: " + t.getMessage(),
									t);
							exceptions.put(line, e);
						}
					}
				}
			} finally {
				reader.close();
			}
		} catch (Throwable t) {
			//log.error("加载扩展接口:{}失败, 类文件: {}}).",t,type,resourceURL,t);
		}
	}
    /**
     * 加载扩展类的实现
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @param extensionClasses 扩展类
     * @param resourceURL 资源路径
     * @param clazz 类实例对象
     * @param name 扩展名称
     * @since 2019年1月6日下午4:37:31
     */
	private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz,
			String name) throws NoSuchMethodException {
		if (!type.isAssignableFrom(clazz)) {
			throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: "
					+ clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");
		}
		if (isWrapperClass(clazz)) {
			Set<Class<?>> wrappers = cachedWrapperClasses;
			if (wrappers == null) {
				cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
				wrappers = cachedWrapperClasses;
			}
			wrappers.add(clazz);
		} else {
			clazz.getConstructor();
			if (name == null || name.length() == 0) {
				name = findAnnotationName(clazz);
				if (name.length() == 0) {
					throw new IllegalStateException("No such extension name for the class " + clazz.getName()
							+ " in the config " + resourceURL);
				}
			}
			//有可能配置了多个名字
			String[] names = NAME_SEPARATOR.split(name);
			if (names != null && names.length > 0) {
				for (String n : names) {
					if (!cachedNames.containsKey(clazz)) {
						//放入Extension实现类与名称映射的缓存中去，每个class只对应第一个名
						cachedNames.put(clazz, n);
					}
					Class<?> c = extensionClasses.get(n);
					if (c == null) {
						//放入到extensionClasses缓存中去，多个name可能对应一份extensionClasses
						extensionClasses.put(n, clazz);
					} else if (c != clazz) {
						throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on "
								+ c.getName() + " and " + clazz.getName());
					}
				}
			}
		}
	}

	/**
	 * 是否是包装类
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @param clazz
	 * @return true 是
	 * @since 2019年1月6日下午4:40:16
	 */
	private boolean isWrapperClass(Class<?> clazz) {
		try {
			clazz.getConstructor(type);
			return true;
		} catch (NoSuchMethodException e) {
			return false;
		}
	}

	/**
	 *  获取名称
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @since 2020/2/13 10:02
	 * @param clazz 目标类
	 * @return 名称
	 * @version 1.0
	 */
	private String findAnnotationName(Class<?> clazz) {
		Spi extension = clazz.getAnnotation(Spi.class);
		if (extension == null) {
			String name = clazz.getSimpleName();
			if (name.endsWith(type.getSimpleName())) {
				name = name.substring(0, name.length() - type.getSimpleName().length());
			}
			return name.toLowerCase();
		}
		return extension.value();
	}

	/**
	 * 获取ClassLoader
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @return
	 * @since 2019年1月6日下午12:14:11
	 */
	private static ClassLoader findClassLoader() {
		return ExtensionLoader.class.getClassLoader();
	}

	/**
	 * 通过SPI加载当前传入接口延伸的所有实现到map中保存
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @return
	 * @since 2019年1月6日下午4:27:32
	 */
	private Map<String, Class<?>> getExtensionClasses() {
		Map<String, Class<?>> classes = cachedClasses.get();
		if (classes == null) {
			synchronized (cachedClasses) {
				classes = cachedClasses.get();
				if (classes == null) {
					classes = loadExtensionClasses();
					cachedClasses.set(classes);
				}
			}
		}
		return classes;
	}

	/**
	 * 获取默认扩展类实现
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @return
	 * @since 2019年1月6日下午4:41:24
	 */
	public T getDefaultExtension() {
		getExtensionClasses();
		if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) {
			return null;
		}
		return getExtension(cachedDefaultName);
	}

	/**
	 * 查找具有给定名称的扩展名
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @param name
	 * @return
	 * @since 2019年1月6日上午10:11:49
	 */
	public T getExtension(String name) {
		if (name == null || name.length() == 0){
            throw new IllegalArgumentException("Extension name == null");
        }
		if ("true".equals(name)) {
			return getDefaultExtension();
		}
		Holder<Object> holder = cachedInstances.get(name);
		if (holder == null) {
			cachedInstances.putIfAbsent(name, new Holder<>());
			holder = cachedInstances.get(name);
		}
		Object instance = holder.get();
		if (instance == null) {
			synchronized (holder) {
				instance = holder.get();
				if (instance == null) {
					instance = createExtension(name);
					holder.set(instance);
				}
			}
		}
		return (T) instance;
	}
	/**
	 * 创建扩展对象
	 * <p>说明:</p>
	 * <li></li>
	 * @author DuanYong
	 * @since 2020/2/13 10:14
	 * @param name 扩展对象名称
	 * @return 扩展对象
	 * @version 1.0
	 */
	private T createExtension(String name) {
		Class<?> clazz = getExtensionClasses().get(name);
		if (clazz == null) {
			throw findException(name);
		}
		try {
			T instance = (T) EXTENSION_INSTANCES.get(clazz);
			if (instance == null) {
				EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
				instance = (T) EXTENSION_INSTANCES.get(clazz);
                //todo 注册到第三方容器
			}
			return instance;
		} catch (Throwable t) {
			throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
					+ ")  could not be instantiated: " + t.getMessage(), t);
		}
	}
    /**
     * 根据名称查找异常
     * <p>说明:</p>
     * <li></li>
     * @author DuanYong
     * @since 2020/2/13 10:14
     * @param name 名称
     * @return 异常对象
     * @version 1.0
     */
	private IllegalStateException findException(String name) {
		for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {
			if (entry.getKey().toLowerCase().contains(name.toLowerCase())) {
				return entry.getValue();
			}
		}
		StringBuilder buf = new StringBuilder("No such extension " + type.getName() + " by name " + name);

		int i = 1;
		for (Map.Entry<String, IllegalStateException> entry : exceptions.entrySet()) {
			if (i == 1) {
				buf.append(", possible causes: ");
			}
			buf.append("\r\n(");
			buf.append(i++);
			buf.append(") ");
			buf.append(entry.getKey());
			buf.append(":\r\n");
			buf.append(entry.getValue());
		}
		return new IllegalStateException(buf.toString());
	}
}
